Swift 3 Custom Operator

Swift 3 出來之後,之前參考別人寫 autolayout + custom operator 部分也跟著不能運作。

當然光靠內建 convert 是行不通的,最好的方式還是去看改版到底改了什麼。

於是就翻了 Xcode Release Notes

The syntax for adding new operators has changed significantly. The new language model uses a more semantic model based on named precedence groups rather than magic numbers. This only affects declaring new operators such as >>> and not adding new overloads of existing operators such as ==. (SE-0077)

custom operator 語法有顯著的改變。

新語言模組是使用更有語義的模組,並基於 precedence groups 而非 magic numbers

這只會影響到 custom operator,而不會影響既有的 operator(overloads)

詳細請看 proposal : SE-0077


首先先回顧一下 Swift 2 之前 custom operator 定義跟使用方式,以及剛剛提到的 magic numbers

可參考 NSHIPSTER:swift-operators

定義部分:

  • 決定:前(prefix) 中(infix) 後置(postfix)
    範例:infix
  • 符號
    範例:<~
  • 結合律:左至右 右至左
    範例:left
  • 優先度:越大越先做
    範例:125
    Swift 2
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    // 定義 custom operator
    // 中置, 結合律:左至右,優先度 125
    infix operator <~ {
    associativity left
    precedence 125 // magic numbers 150
    }

    // 定義 custom operator 功能
    public func <~ (left: NSLayoutConstraint, right: UILayoutPriority) -> NSLayoutConstraint {
    left.priority = right
    return left
    }

回過來看 Swift 3 該怎麼改,首先先回顧原本的 magic numbers 125

對照優先度: 120 < 125 < 130

再對照符號: && < <~ < ==

再對照 Swift 2: Conjunctive < target < Comparative

最後是 Swift 3: LogicalConjunctionPrecedence < target < ComparisonPrecedence

於是我們知道目標是處於 LogicalConjunctionPrecedence < target < ComparisonPrecedence 之間

這時才來看他所謂的更有語義的模組 precedence groups

首先需要先定義 custom precedencegroup 並給上名稱 precedencegroup YumeComparisonAssignmentPrecedence

因為目標是 infix,所以一樣要定義結合律 associativity: left

接著是有語義的比大小

  • lowerThan: ComparisonPrecedence
  • higherThan: LogicalConjunctionPrecedence
Swift 3 定義 custom precedencegroup
1
2
3
4
5
precedencegroup YumeComparisonAssignmentPrecedence {
associativity: left
lowerThan: ComparisonPrecedence
higherThan: LogicalConjunctionPrecedence
}

然後定義你的 custom operator 要使用哪一種 Precedence

Swift 3
1
infix operator <~ : YumeComparisonAssignmentPrecedence

最後是實現功能

Swift 3
1
2
3
4
5
6
extension NSLayoutConstraint {
static public func <~ (left: NSLayoutConstraint, right: UILayoutPriority) -> NSLayoutConstraint {
left.priority = right
return left
}
}

小抱怨:預設 Precedence 必須到 Standard library changes 查找,有點麻煩。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
precedencegroup BitwiseShiftPrecedence {
higherThan: MultiplicationPrecedence
}

precedencegroup FunctionArrowPrecedence {
associativity: right
}

precedencegroup MultiplicationPrecedence {
associativity: left
higherThan: AdditionPrecedence
}

precedencegroup TernaryPrecedence {
associativity: right
higherThan: AssignmentPrecedence
}

precedencegroup DefaultPrecedence {
higherThan: TernaryPrecedence
}

precedencegroup LogicalDisjunctionPrecedence {
associativity: left
higherThan: TernaryPrecedence
}

precedencegroup LogicalConjunctionPrecedence {
associativity: left
higherThan: LogicalDisjunctionPrecedence
}

precedencegroup ComparisonPrecedence {
higherThan: LogicalConjunctionPrecedence
}

precedencegroup NilCoalescingPrecedence {
associativity: right
higherThan: ComparisonPrecedence
}

precedencegroup AdditionPrecedence {
associativity: left
higherThan: RangeFormationPrecedence
}

precedencegroup CastingPrecedence {
higherThan: NilCoalescingPrecedence
}

precedencegroup AssignmentPrecedence {
associativity: right
assignment: true
higherThan: FunctionArrowPrecedence
}

precedencegroup RangeFormationPrecedence {
higherThan: CastingPrecedence
}